#!/usr/bin/env python

from sys import *
import masterboard

OPTS, ARGS = None, None
BOARD = None


class Fract:
    def __init__(self, z, n):
        self.z = z
        self.n = n
    def __str__(self):
        return "%d/%d (=%f)" % (self.z, self.n, float(self.z)/float(self.n))

def print_situation(hex, army, wkeit):
    print hex, army, wkeit


def _moves(hex, die, prev=None):
    if die==0:
        yield hex
        return
    # find must-follows on first step
    if prev==None: # first stel
        for ex in hex.exits:
            if ex.type == 'BLOCK': # must follow on first step
                for m in _moves(BOARD[ex.label], die-1, hex):
                    yield m
                return
    # may follows and later steps
    for ex in hex.exits:
        if ex.type == 'BLOCK':
            continue # already handled on prev==None, not allowed to follow in later steps
        if ex.type == 'ARCH':
            if prev==None: # may follow on first step only
                for m in _moves(BOARD[ex.label], die-1, hex):
                    yield m
            continue
        if prev and prev.label == ex.label: # dont backtrack
            continue
        if ex.type == 'ARROW':  # many choices in tower, used also in inner ring
            for m in _moves(BOARD[ex.label], die-1, hex):
                yield m
            continue
        if ex.type == 'ARROWS':  # just follow
            for m in _moves(BOARD[ex.label], die-1, hex):
                yield m
            continue
        assert False, "unknown exit type %s" % ex.type


def moves(hex, die):
    return set( _moves(hex, die, None) )

######################################################################

MUSTER = {
    'M': {
        'oo' : 't',   # 2 Ogre muster 1 Troll
        'tt' : 'r',   # 2 Troll muster 1 Ranger
        'o'  : 'o',   # 1 Ogre can also find 1 Ogre
        't'  : 'to',  # 1 Troll can also find 1 Troll or 1 Ogre
        'r'  : 'rto', # 1 Ranger can find 1 Ranger, 1 Troll or 1 Ogre
        },
    'B' : {
        'gg' : 'y',
        'yy' : 'G',
        'g'  : 'g',
        'y'  : 'yg',
        'G'  : 'Gyg',
        },
    'P' : { 'cc':'l', 'll':'r',  'c':'c',  'l':'lc', 'r':'rlc' },
    'H' : { 'ooo':'m', 'mm':'u', 'o':'o',  'm':'mo', 'u':'umo' },
    'S' : { 'ttt':'v', 'vv':'H', 't':'t',  'v':'vt', 'H':'Hvt' },
    'W' : { 'ccc':'w', 'ww':'u', 'c':'c',  'w':'wc', 'u':'uwc' },
    'D' : { 'lll':'F', 'FF':'H', 'l':'l',  'F':'Fl', 'H':'HFl' },
    'J' : { 'yyy':'B', 'BB':'S', 'y':'y',  'B':'By', 'S':'SBy' },
    'm' : { 'll':'m', 'mm':'D', 'DD':'C', 'l':'l', 'm':'ml', 'D':'Dml', 'C':'CDml' },
    't' : { 'tt':'w', 'ww':'I', 'II':'C', 't':'t', 'w':'wt', 'I':'Iwt', 'C':'CIwt' },
    'T' : { 'T':'k', '':'cgo', 'ccc':'u', 'ooo':'u', 'ggg':'u', 'u':'u', 'k':'k',
            'ttt':'u','rrr':'u','yyy':'u','lll':'u','GGG':'u', # more...? dont make sense prob.
            },
    }

CRIT2STRING = {
    'o':'Ogr', 't':'Tro', 'r':'Ran', 'g':'Gar', 'y':'Cyc', 'G':'Gor',
    'l':'Lio', 'm':'Min', 'u':'Uni', 'w':'Wbe', 'H':'Hyd', 'v':'Wyv',
    'F':'Grf', 'B':'Beh', 'S':'Ser', 'D':'Dra', 'I':'Gia', 'C':'Col',
    'T':'Tit', 'k':'Wlk', 'a':'Ang', 'A':'Arc', 'u':'Gua',
}

CRIT2VALUE = {
    'o':2*6  , 't':2*8  , 'r':4*4+2, 'g':4*3  , 'y':2*9  , 'G':3*6+2,
    'l':5*3  , 'm':4*4+1, 'u':6*4  , 'w':6*3  , 'H':10*3+1, 'v':7*3+1,
    'F':7*3+1, 'B':8*3  , 'S':18*2 , 'D':9*3+2, 'I':7*4+1, 'C':10*4 ,
    'T':6*4+1, 'k':5*4+2, 'a':6*4+2, 'A':9*4+2, 'u':12*2+1,
}

def armyToValue(army):
    return sum([ CRIT2VALUE[c]  for c in army ])

def armyToString(army):
    return ",".join([ CRIT2STRING[c]  for c in army ]) + "=%d" % armyToValue(army)

def muster(hex, army):
    yield army # there is always the option not to muster
    if len(army) == 7:
        return # max stack size
    tbl = MUSTER[hex.getTerrainId()]
    for mustering, musters in tbl.items():
        if army.find(mustering) > 0:
            for critter in musters:
                yield "".join( sorted(army+critter) )

######################################################################

def roll(hex, army, wkeit):
    print_situation(hex, army, wkeit)
    for die in [1,2,3,4,5,6]:
        newhexes = moves(hex, die)
        for newhex in newhexes:
            newarmy = muster(newhex, army)
            print die, newhex, map(armyToString, newarmy)

######################################################################

def main(hexnum, army="ooggT"):
    global BOARD
    BOARD = masterboard.read_board(OPTS.map_file)
    roll(BOARD[hexnum], "".join(sorted(army)), Fract(1,1))

######################################################################

def demo_1():
    global BOARD
    BOARD = masterboard.read_board(OPTS.map_file)
    print "M140 [0]", moves(BOARD["140"], 0)
    print "M140 [1]", moves(BOARD["140"], 1)
    print "M140 [2]", moves(BOARD["140"], 2)
    print "M140 [3]", moves(BOARD["140"], 3)
    print "M140 [4]", moves(BOARD["140"], 4)
    print "B141 [1]", moves(BOARD["141"], 1)
    print "B141 [2]", moves(BOARD["141"], 2)
    print "B141 [3]", moves(BOARD["141"], 3)
    print "J40 [1]", moves(BOARD["40"], 1)
    print "J40 [2]", moves(BOARD["40"], 2)
    print "J40 [3]", moves(BOARD["40"], 3)
    print "B3 [1]", moves(BOARD["3"], 1)
    print "B3 [2]", moves(BOARD["3"], 2)
    print "B3 [3]", moves(BOARD["3"], 3)
    print "B3 [4]", moves(BOARD["3"], 4)

def demo_2():
    global BOARD
    BOARD = masterboard.read_board(OPTS.map_file)
    print list(muster(BOARD["141"], "ooggT"))

######################################################################

if __name__ == "__main__":
    from optparse import OptionParser
    parser = OptionParser()
    pa = parser.add_option
    # options
    pa("-M", "--map-file", default="DefaultMap.xml")
    pa("-D", "--demo", default="")
    # parse
    (OPTS, ARGS) = parser.parse_args()
    # call
    if len(ARGS) > 0:
        print >>stderr, "sample call: %s 140" % argv[0]
        main(hexnum = ARGS[0])
    else:
        if OPTS.demo == "1":
            demo_1()
        elif OPTS.demo == "2":
            demo_2()
        else:
            main(hexnum = "20")


